package groot.dozer.mapper;

import groot.dozer.annotation.ClassMapping;
import groot.dozer.common.AnnotationParse;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import org.dozer.DozerBeanMapper;
import org.dozer.MappingException;
import org.dozer.classmap.ClassMappings;
import org.dozer.classmap.MappingFileData;
import org.dozer.loader.CustomMappingsLoader;
import org.dozer.loader.LoadMappingsResult;
import org.dozer.loader.xml.XMLParser;
import org.w3c.dom.Document;

/**
 * @author Groot
 * @since 2018/11/7 10:59
 *
 * @see AnnotationParse
 * @see DozerBeanMapper
 */
public class ReadableDozerBeanMapper extends DozerBeanMapper {

  /**
   * dozer最终所有的映射信息都会存在这个对象仲
   */
  private ClassMappings classMappings;

  public ReadableDozerBeanMapper() {
    super();
    super.getMappingProcessor();
    initReadble();
  }

  public ReadableDozerBeanMapper(List<String> mappingFiles) {
    super(mappingFiles);
    super.getMappingProcessor();
    initReadble();
  }

  @Override
  public void setMappingFiles(List<String> mappingFileUrls) {
    super.setMappingFiles(mappingFileUrls);
    super.getMappingProcessor();
    initReadble();
  }

  @Override
  public <T> T map(Object source, Class<T> destinationClass) throws MappingException {
    Class mappingClass = getMappingClass(source.getClass(), destinationClass);
    preClassMappings(mappingClass);
    return getMappingProcessor().map(source, destinationClass);
  }

  @Override
  public void map(Object source, Object destination) throws MappingException {
    Class mappingClass = getMappingClass(source.getClass(), destination.getClass());
    preClassMappings(mappingClass);
    getMappingProcessor().map(source, destination);
  }

  @Override
  public <T> T map(Object source, Class<T> destinationClass, String mapId) throws MappingException {
    Class mappingClass = getMappingClass(source.getClass(), destinationClass);
    preClassMappings(mappingClass);
    return getMappingProcessor().map(source, destinationClass, mapId);
  }

  @Override
  public void map(Object source, Object destination, String mapId) throws MappingException {
    Class mappingClass = getMappingClass(source.getClass(), destination.getClass());
    preClassMappings(mappingClass);
    getMappingProcessor().map(source, destination, mapId);
  }

  /**
   * 进行初始化的工作，提前解析xml文件，以获取classMappings
   */
  private void initReadble() {
    try {
      // 在启动时主动触发完成配置文件的加载解析工作，dozer默认在第一次使用时才完成解析xml的动作，此处将其提前

      // 暴力反射，获取父类中的customMappings对象，存储到类属性中
      Class<?> superclass = this.getClass().getSuperclass();
      Field customMappings = superclass.getDeclaredField("customMappings");
      customMappings.setAccessible(true);
      classMappings = (ClassMappings) customMappings.get(this);
      if (classMappings == null) {
        classMappings = new ClassMappings();
      }

    } catch (Exception e) {
      e.printStackTrace();
    }
  }

  /**
   * 映射前准备mappingClass
   */
  private void preClassMappings(Class mappingClass) {
    if (mappingClass != null) {
      // 根据annotation创建xml
      Document document = parseClass(mappingClass);
      // 加入mappings
      XMLParser parser = new XMLParser();
      MappingFileData read = parser.read(document);
      CustomMappingsLoader loader = new CustomMappingsLoader();
      ArrayList arrayList = new ArrayList();
      arrayList.add(read);
      LoadMappingsResult load = loader.load(arrayList);
      ClassMappings customMappings = load.getCustomMappings();
      classMappings.addAll(customMappings);
    }
  }

  /**
   * 把注解解析成document
   */
  private Document parseClass(Class mappingClass) {

    AnnotationParse parse = new AnnotationParse();
    Document document = parse.parseClass(mappingClass);

    return document;


  }

  /**
   * 查找要映射的源头类
   */
  private Class getMappingClass(Class sourceClass, Class destinationClass) {
    if (checkMapping(sourceClass, destinationClass)) {
      return sourceClass;
    }
    if (checkMapping(destinationClass, sourceClass)) {
      return destinationClass;
    }
    return null;


  }


  /**
   * 检查是否需要映射
   */
  private boolean checkMapping(Class sourceClass, Class destinationClass) {
    Annotation sourAnnotation = sourceClass.getAnnotation(ClassMapping.class);
    if (sourAnnotation != null) {
      ClassMapping sourMappings = (ClassMapping) sourAnnotation;
      Class<?> destClass = sourMappings.value();
      if (destClass.equals(destinationClass)) {
        if (sourMappings.isSource()) {
          return !classMappings.contains(sourceClass, destinationClass, null);
        } else {
          return !classMappings.contains(destinationClass, sourceClass, null);
        }
      }
    }

    return false;
  }
}
